#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
using namespace std;

namespace Server
{
    enum
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        OPEN_ERR,
    };
    static const string defaultIP = "0.0.0.0"; // Todo  接受任意IP
    static const int gNum = 1024;

    typedef function<void(int, uint16_t, string, string)> func_t; // fd,port,ip,message
    class UdpServer
    {
    public:
        UdpServer(const func_t &callback, const uint16_t &port, const string &ip = defaultIP)
            : _callback(callback), _port(port), _ip(ip), _serverfd(-1)
        {
        }
        void InitServer()
        {
            // 创建套接字  a file descriptor for the new socket is returned.
            // 网络通信 AF_INET   UDP服务类型 -》SOCK_DGRAM  protocol-> 套接字协议类别 设置为0即可
            _serverfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (-1 == _serverfd) // 创建失败
            {
                cerr << "Server err:" << errno << ":" << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }
            cout << "Socket success:" << _serverfd << endl;
            // 绑定服务端的IP(any IP)和端口号
            struct sockaddr_in addr; // 16位地址AF_INET  16位端口号，32位IP地址  sin_family
            bzero(&addr, sizeof(addr));
            addr.sin_family = AF_INET;                     // sin_##family
            addr.sin_port = htons(_port);                  // 这里的port需要进入网络，转换为大端
            addr.sin_addr.s_addr = inet_addr(_ip.c_str()); //_ip需要转化为uint32_t 再转化为大端  inet_addr -> 完成两个的连续转化

            int bret = bind(_serverfd, (struct sockaddr *)&addr, sizeof(addr)); // 结构体的大小
            if (-1 == bret)                                                     // 创建失败
            {
                cerr << "Bind err:" << errno << ":" << strerror(errno) << endl;
                exit(BIND_ERR);
            }
        }

        void StartServer()
        {
            // 服务器就是一个死循环
            char buffer[gNum]; // C++的语法
            while (1)
            {
                struct sockaddr_in peer; // 把读取到的数据存储到这里面
                socklen_t len = sizeof(peer);
                // 从套接字中收到数据  返回值是读到的字节数  0就是阻塞式获取  还有获取客户端的struct sockaddr 输出型参数
                ssize_t sret = recvfrom(_serverfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
                if (sret > 0)
                {
                    buffer[sret] = 0;
                    // 读取成功
                    string clientIP = inet_ntoa(peer.sin_addr); // 网络序列（大小端），-> int -》点分十进制
                    uint16_t clientPort = ntohs(peer.sin_port); // ntohs  网络转主机
                    // 数据信息
                    string message = buffer;
                    // 打印信息
                    cout << "【" << clientIP << "】"
                         << "[" << clientPort << "]#-> " << message << endl;
                    _callback(_serverfd, clientPort, clientIP, message); // 发送的是client端的ip与端口号
                }
            }
        }
        ~UdpServer()
        {
        }

    private:
        uint16_t _port;   // 端口号
        string _ip;       // ip地址
        int _serverfd;    // 返回的文件描述符
        func_t _callback; // 回调函数
    };
}
